package com.jichangxiu.framework.handler;

import cn.hutool.core.util.ObjectUtil;
import com.jichangxiu.common.annotation.Delete;
import com.jichangxiu.common.annotation.Get;
import com.jichangxiu.common.annotation.Post;
import com.jichangxiu.common.annotation.Put;
import com.jichangxiu.common.constant.ErrorCode;
import com.jichangxiu.common.entity.bo.Record;
import com.jichangxiu.common.entity.bo.Result;
import com.jichangxiu.common.exception.CaptchaException;
import com.jichangxiu.common.exception.FrameworkException;
import com.jichangxiu.common.exception.ServerException;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.*;
import com.jichangxiu.framework.factory.AsyncFactory;
import com.jichangxiu.framework.service.LogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

/**
 * GlobalExceptionHandler:
 *
 * @author Jcx
 * @create 2023-04-04 10:03
 */
@Slf4j
@ResponseBody
@RestControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {

    @Resource
    private TokenUtils tokenUtils;

    private LogService logService;

    @Autowired
    public void setLogService(LogService logService) {
        this.logService = logService;
    }

    @ExceptionHandler(Exception.class)
    public Result<String> error(Exception e) {
        e.printStackTrace();
        return Result.no(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(RuntimeException.class)
    public Result<String> error(RuntimeException e) {
        e.printStackTrace();
        return Result.no(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(value = AccessDeniedException.class)
    public Result<String> accessDeniedException(HttpServletRequest httpServletRequest, AccessDeniedException e) {
        e.printStackTrace();
        Record logRecord = null;
        try {
            logRecord = getLogRecord(httpServletRequest);
            logRecord.put("exception", e.getMessage());
            logRecord.put("status", false);
        } catch (Exception ignored) {
        } finally {
            try {
                Annotation[] annotations = SpringUtils.getHandlerRequestAnnotation(httpServletRequest);
                boolean logEnabled = false;
                if (ObjectUtil.isNotEmpty(annotations)) {
                    for (Annotation annotation : annotations) {
                        if (annotation instanceof Post) {
                            Post post = (Post) annotation;
                            logEnabled = post.logEnabled();
                        } else if (annotation instanceof Delete) {
                            Delete delete = (Delete) annotation;
                            logEnabled = delete.logEnabled();
                        } else if (annotation instanceof Put) {
                            Put put = (Put) annotation;
                            logEnabled = put.logEnabled();
                        } else if (annotation instanceof Get) {
                            Get get = (Get) annotation;
                            logEnabled = get.logEnabled();
                        }
                    }
                }
                if (logEnabled) {
                    // 保存数据库
                    AsyncUtils.getINSTANCE().execute(AsyncFactory.recordPlatformLog(logService, logRecord));
                }
            } catch (Exception ex) {
                log.error("请求无权限访问日志记录异常", ex);
            }
        }
        return Result.no(ErrorCode.FORBIDDEN);
    }

    @ExceptionHandler(ServerException.class)
    public Result<Object> serverException(ServerException e) {
        e.printStackTrace();
        return Result.no(ErrorCode.INTERNAL_SERVER_ERROR).MSG(e.getMsg());
    }

    @ExceptionHandler(FrameworkException.class)
    public Result<Object> frameworkException(FrameworkException e) {
        e.printStackTrace();
        return Result.no(ErrorCode.FRAMEWORK_SERVER_ERROR).MSG(e.getMsg());
    }

    @ExceptionHandler(CaptchaException.class)
    public Result<Object> captchaException(CaptchaException e) {
        e.printStackTrace();
        return Result.no(ErrorCode.CAPTCHA_ERROR).MSG(e.getMsg());
    }

    /**
     * 业务异常
     *
     * @param e 异常信息
     * @return 格式化返回数据
     */
    @ExceptionHandler(ServiceException.class)
    public Result<Object> serviceException(ServiceException e) {
        e.printStackTrace();
        return Result.no(ErrorCode.SERVICE_EXCEPTION).MSG(e.getMsg());
    }

    private Record getLogRecord(HttpServletRequest httpServletRequest) throws Exception {
        HandlerExecutionChain handlerChain = SpringUtils.getRequestHandler(httpServletRequest);
        HandlerMethod handler = (HandlerMethod) handlerChain.getHandler();
        // 处理请求的类
        String name = handler.getBean().getClass().getName();
        String className = name.split("\\$\\$")[0];
        // 处理请求的方法
        String methodName = handler.getMethod().getName();
        String url = httpServletRequest.getRequestURI();
        String ip = IpUtils.getIpAddr(httpServletRequest);

        Record record = Record.getRecord();
        String method = httpServletRequest.getMethod();
        if (HttpMethod.POST.matches(method)) {
            record.put("serviceType", "增加");
            record.put("operatorType", "ADD");
        } else if (HttpMethod.DELETE.matches(method)) {
            record.put("serviceType", "删除");
            record.put("operatorType", "DELETE");
        } else if (HttpMethod.PUT.matches(method)) {
            record.put("serviceType", "修改");
            record.put("operatorType", "EDIT");
        } else if (HttpMethod.GET.matches(method)) {
            record.put("serviceType", "查询");
            record.put("operatorType", "QUERY");
        } else {
            record.put("serviceType", "其他");
            record.put("operatorType", "OTHER");
        }
        record.put("httpMethod", method);
        record.put("model", className);
        record.put("method", methodName);
        record.put("url", url);
        record.put("ip", ip);
        record.put("location", IpUtils.getRealAddress(ip));
        record.put("param", "没有操作权限");
        try {
            record.put("userId", tokenUtils.getSecurityUser().getUserId());
            record.put("tenantId", tokenUtils.getTenantId());
        } catch (Exception exception) {
            record.put("userId", "未登录用户");
            record.put("tenantId", "未登录用户");
        }
        return record;
    }

}
